/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package mapmakerapp;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.util.ArrayList;
import javax.imageio.ImageIO;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JFileChooser;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;


public class MapPanel extends JPanel implements MouseListener, MouseMotionListener{
    public static final int PANEL_OFFSET_X = 8;//10;
    public static final int PANEL_OFFSET_Y = 8;//25;
    
    public static final Color BOUND_COLOR = Color.RED;
    
    private ArrayList<MapItem> mapItems;

    private int maxWidth;
    private int maxHeight;
    
    private int currentIndex = -1;
    private int currentIndex4Popup = -1;
    private int oldX;
    private int oldY;

    public boolean useBound = true;    

    private JPopupMenu popUpMenu;
    private MyFrame frame;
    
    public MapPanel(MyFrame frame) {
       super();

       this.frame = frame;
       mapItems = new ArrayList<MapItem>();
       this.addMouseListener(this);
       this.addMouseMotionListener(this);

       addPopupMenu();
    }

    private void addPopupMenu()
    {
        popUpMenu = new JPopupMenu();

        JMenuItem itemReplace = new JMenuItem("Replace");
        itemReplace.addActionListener(actionReplace);
        popUpMenu.add(itemReplace);

        JMenuItem itemDelete = new JMenuItem("Delete");
        itemDelete.addActionListener(actionDelete);
        popUpMenu.add(itemDelete);
        
        JMenu menuFlip = new JMenu("Flip");
        JMenuItem item1 = new JMenuItem("Flip Horizontal");
        item1.addActionListener(actionFlipH);
        menuFlip.add(item1);

        JMenuItem item2 = new JMenuItem("Flip Vertical");
        item2.addActionListener(actionFlipV);
        menuFlip.add(item2);

        JMenuItem item22 = new JMenuItem("Flip None");
        item22.addActionListener(actionFlipNone);
        menuFlip.add(item22);
        popUpMenu.add(menuFlip);

        JMenuItem item3 = new JMenuItem("Rotate");
        item3.addActionListener(actionRotate);
        popUpMenu.add(item3);

        JMenuItem item4 = new JMenuItem("Scale");
        item4.addActionListener(actionScale);
        popUpMenu.add(item4);

        JMenu menuInfo = new JMenu("Info");
        JMenuItem itemInfo1 = new JMenuItem("Object Name");
        itemInfo1.addActionListener(actionObjectName);
        menuInfo.add(itemInfo1);

        JMenuItem itemInfo2 = new JMenuItem("Description");
        itemInfo2.addActionListener(actionDesc);
        menuInfo.add(itemInfo2);
        popUpMenu.add(menuInfo);
    }

    @Override
    public void paint(Graphics g) {
        super.paint(g);

        int count = getCountItems();
        MapItem item;
        BufferedImage imgTemp;
        int newW, newH;
        
        g.setColor(BOUND_COLOR);
        for(int i=0; i < count; i++)
        {
            item = mapItems.get(i);
            imgTemp = item.image;

            newW = imgTemp.getWidth();
            newH = imgTemp.getHeight();
            
            if(i > 0)
            {
                imgTemp = ImageTools.imageFlip(item.image, item.flip);
                imgTemp = ImageTools.imageScale(imgTemp, item.scale);

                newW = imgTemp.getWidth();
                newH = imgTemp.getHeight();
                imgTemp = ImageTools.imageRotate(imgTemp, item.rotate);

                //System.out.println("img:" + imgTemp);
                //set new image for item
                /*if(item.flip != ImageTools.FLIP_NONE)
                {
                    item.image = imgTemp;
                    mapItems.set(i, item);
                }*/
            }
            // --
            g.drawImage(imgTemp, PANEL_OFFSET_X + item.topleft.x, PANEL_OFFSET_Y + item.topleft.y, null);
            if(i > 0 && useBound)
            {
                ImageTools.drawRect(PANEL_OFFSET_X + item.topleft.x, PANEL_OFFSET_Y + item.topleft.y,
                        newW, newH, (Graphics2D)g, item.rotate);
                //g.drawRect(PANEL_OFFSET_X + item.topleft.x, PANEL_OFFSET_Y + item.topleft.y,
                //        imgTemp.getWidth(), imgTemp.getHeight());           
            }
        }        
    }

    public boolean setItem(int index, String path, String desc, String objectName, int x, int y, int flip, int rotate, float scale)
    {
        try
        {
            File f = new File(path);
            String fileName = f.getName();
            
            if(!f.exists())
            {
                String pathFileSave = this.frame.getPathFileSave();
                File fSave = new File(pathFileSave);
                f = new File(fSave.getParentFile(), fileName);                
            }
            
            BufferedImage img = null;
            img = ImageIO.read(f);
            if(img == null) return false;
            
            if(index < 0)
                return false;

            if(index < getCountItems())
            {
                MapItem originalItem = MapItem.copy(mapItems.get(index));
                originalItem.set(img, path, desc, objectName, new Point(x, y), flip, rotate, scale);
                mapItems.set(index, originalItem);
            }
            else
            {
                mapItems.add(new MapItem(img, path, desc, objectName, new Point(x,y), flip, rotate, scale));
            }

            if(index == 0)
            {
                //background
                maxWidth = img.getWidth();
                maxHeight = img.getHeight();

                setFrameSize(img);
            }
        }
        catch(Exception ex)
        {
            ex.printStackTrace();
            return false;
        }

        return true;
    }

    public int getCountItems()
    {
        return mapItems.size();
    }

    public MapItem getMapItem(int index)
    {
        if(index >=0 && index < mapItems.size())
            return mapItems.get(index);

        return null;
    }

    public void deleteMap()
    {
        mapItems.removeAll(mapItems);
    }

    public void setFrameSize(BufferedImage image)
    {
        this.frame.setSize(image.getWidth() + MapPanel.PANEL_OFFSET_X*2 + frame.getInsetDimension().width,
                            image.getHeight() + MapPanel.PANEL_OFFSET_Y*2 + (frame.getInsetDimension()).height);
    }
    
    private int getItem(int x, int y)
    {
        int count = getCountItems();
        MapItem item;
        int itemX, itemY;
        
        for(int i=count - 1; i > 0; i--)
        {
            item = mapItems.get(i);
            itemX = item.topleft.x + PANEL_OFFSET_X;
            itemY = item.topleft.y + PANEL_OFFSET_Y;
            
            if(x >= itemX && x <= itemX + item.image.getWidth()*item.scale
                 && y >= itemY && y <= itemY + item.image.getHeight()*item.scale)
            {
                return i;
            }
        }        
        return -1;
    }
    
    public void mouseClicked(MouseEvent e) {       
        return;
    }

    public void mousePressed(MouseEvent e) {
        int x = e.getX();
        int y = e.getY();

        if(e.isPopupTrigger())
        {
            currentIndex4Popup = getItem(x, y);
            if(currentIndex4Popup > 0)
                popUpMenu.show(e.getComponent(), x, y);
            
            return;
        }
                
        currentIndex = getItem(x, y);
        oldX = x;
        oldY = y;
    }

    public void mouseReleased(MouseEvent e) {
        int x = e.getX();
        int y = e.getY();

        if(e.isPopupTrigger())
        {
            currentIndex4Popup = getItem(x, y);
            if(currentIndex4Popup > 0)
                popUpMenu.show(e.getComponent(), x, y);

            return;
        }
        
        if(currentIndex >= 0)
        {           
            currentIndex = -1;
        }
    }

    public void mouseEntered(MouseEvent e) {
        return;
    }

    public void mouseExited(MouseEvent e) {
        return;
    }

    public void mouseDragged(MouseEvent e) {
        if(currentIndex > 0)
        {
            int x = e.getX();
            int y = e.getY();
            float width, height;
            
            MapItem originalItem = MapItem.copy(mapItems.get(currentIndex));
            width = originalItem.image.getWidth()*originalItem.scale;
            height = originalItem.image.getHeight()*originalItem.scale;
            
            originalItem.topleft.x += x - oldX;
            originalItem.topleft.y += y - oldY;

            if(originalItem.topleft.x < 0)
                originalItem.topleft.x = 0;
            if(originalItem.topleft.x + width > maxWidth - 1)
                originalItem.topleft.x = maxWidth - 1 - (int)width;

            if(originalItem.topleft.y < 0)
                originalItem.topleft.y = 0;
            if(originalItem.topleft.y + height > maxHeight - 1)
                originalItem.topleft.y = maxHeight - 1 - (int)height;
            
            oldX = x;
            oldY = y;

            mapItems.set(currentIndex, originalItem);

            updateUI();
        }
    }

    public void mouseMoved(MouseEvent e) {
        return;
    }

    private void replaceItem()
    {
        try
         {
            if(currentIndex4Popup <= 0)
                return;
            
            String filename = File.separator + "tmp";
            JFileChooser fc = new JFileChooser(new File(filename));

            fc.showOpenDialog(this);
            File selFile = fc.getSelectedFile();
            //System.out.println(selFile.getAbsolutePath());

            if(selFile == null)
            {
                return ;
            }

            MapItem  oldItem = mapItems.get(currentIndex4Popup);
            this.setItem(currentIndex4Popup, selFile.getAbsolutePath(), oldItem.desc, oldItem.objectName, oldItem.topleft.x, oldItem.topleft.y
                    , 0, 0, 1);
            
            currentIndex4Popup = -1;
        }
        catch(Exception ex)
        {
            ex.printStackTrace();
        }
    }

    private void deleteItem()
    {
        if(currentIndex4Popup <= 0)
                return;

        int result = JOptionPane.showConfirmDialog(this.frame, "Want delete this item ?", "Delete", JOptionPane.YES_NO_OPTION);
        if(result != JOptionPane.YES_OPTION)
            return;
        
        try
         {            
            mapItems.remove(currentIndex4Popup);
            currentIndex4Popup = -1;
        }
        catch(Exception ex)
        {
            ex.printStackTrace();
        }
    }

    private void flipItem(int typeFlip)
    {
        if(currentIndex4Popup <= 0)
            return;

        try
        {
            MapItem  oldItem = mapItems.get(currentIndex4Popup);            
            oldItem.flip = typeFlip;
            mapItems.set(currentIndex4Popup, oldItem);

            currentIndex4Popup = -1;
        }
        catch(Exception ex)
        {
            ex.printStackTrace();
            return;
        }
    }

    private void rotateItem()
    {
        if(currentIndex4Popup <= 0)
                    return;

        try
        {
            int degree = 0;
            String str = JOptionPane.showInputDialog(null, "Enter degree : ","Rotate", 1);
            if(str != null)
                degree = Integer.valueOf(str);

            if(degree >= 0 && degree < 360)
            {
                MapItem  oldItem = mapItems.get(currentIndex4Popup);
                oldItem.rotate = degree;
                mapItems.set(currentIndex4Popup, oldItem);
            }

            currentIndex4Popup = -1;
        }
        catch(Exception ex)
        {
            ex.printStackTrace();
            return;
        }
    }

    private void scaleItem()
    {
        if(currentIndex4Popup <= 0)
            return;

        try
        {
            float scale = 1;
            String str = JOptionPane.showInputDialog(null, "Enter scale (<5): ","Scale", 1);
            if(str != null)
                scale = Float.valueOf(str);

            if(scale > 0 && scale < 5)
            {
                MapItem  oldItem = mapItems.get(currentIndex4Popup);
                oldItem.scale = scale;
                mapItems.set(currentIndex4Popup, oldItem);
            }

            currentIndex4Popup = -1;
        }
        catch(Exception ex)
        {
            ex.printStackTrace();
            return;
        }
    }

    private void updateObjectName4Item()
    {
        if(currentIndex4Popup <= 0)
            return;

        try
        {
            MapItem  oldItem = mapItems.get(currentIndex4Popup);
            String str = (String) JOptionPane.showInputDialog(null, "Enter Object Name: ","Object Name", 1, null, null, oldItem.objectName);
            if(str != null)                
            {
                //MapItem  oldItem = mapItems.get(currentIndex4Popup);
                oldItem.objectName = str;
                mapItems.set(currentIndex4Popup, oldItem);
            }

            currentIndex4Popup = -1;
        }
        catch(Exception ex)
        {
            ex.printStackTrace();
            return;
        }
    }

    private void updateDescription4Item()
    {
        if(currentIndex4Popup <= 0)
            return;

        try
        {
            MapItem  oldItem = mapItems.get(currentIndex4Popup);
            
            String str = (String) JOptionPane.showInputDialog(null, "Enter Description: ","Description", 1, null, null,oldItem.desc);
            if(str != null)
            {
                //MapItem  oldItem = mapItems.get(currentIndex4Popup);
                oldItem.desc = str;
                mapItems.set(currentIndex4Popup, oldItem);
            }

            currentIndex4Popup = -1;
        }
        catch(Exception ex)
        {
            ex.printStackTrace();
            return;
        }
    }
    //--
    // The action
    public Action actionFlipH = new AbstractAction("FlipH") {
        public void actionPerformed(ActionEvent evt) {
            // Perform action
            flipItem(ImageTools.FLIP_HORIZONTAL);
            updateUI();
        }
    };

    public Action actionFlipV = new AbstractAction("FlipV") {
        public void actionPerformed(ActionEvent evt) {
            // Perform action
            flipItem(ImageTools.FLIP_VERICAL);
            updateUI();
        }
    };

    public Action actionFlipNone = new AbstractAction("FlipNone") {
        public void actionPerformed(ActionEvent evt) {
            // Perform action
            flipItem(ImageTools.FLIP_NONE);
            updateUI();
        }
    };
    
    public Action actionRotate = new AbstractAction("Rotate") {
        public void actionPerformed(ActionEvent evt) {
            // Perform action
            rotateItem();
            updateUI();
        }
    };

    public Action actionScale = new AbstractAction("Scale") {
        public void actionPerformed(ActionEvent evt) {
            // Perform action
            scaleItem();
            updateUI();
        }
    };

    public Action actionReplace = new AbstractAction("Replace") {
        public void actionPerformed(ActionEvent evt) {
            // Perform action
            //System.out.println("Replace");
            replaceItem();
            updateUI();
        }
    };

    public Action actionDelete = new AbstractAction("Delete") {
        public void actionPerformed(ActionEvent evt) {
            // Perform action
            //System.out.println("Delete");
            deleteItem();
            updateUI();
        }
    };

    public Action actionObjectName = new AbstractAction("Object Name") {
        public void actionPerformed(ActionEvent evt) {
            // Perform action
            //System.out.println("Delete");
            updateObjectName4Item();
            updateUI();
        }
    };

    public Action actionDesc = new AbstractAction("Description") {
        public void actionPerformed(ActionEvent evt) {
            // Perform action
            //System.out.println("Delete");
            updateDescription4Item();
            updateUI();
        }
    };
}

class MapItem
{
    public BufferedImage image;
    public String path;
    public String name;
    public String objectName;
    public String desc;

    public Point topleft;
    public int flip;
    public int rotate;
    public float scale;

    public MapItem(BufferedImage image, String path, String desc, String objectName, Point topleft, int flip, int rotate, float scale)
    {
        this.image = image;
        this.path = path;
        this.desc = desc;
        this.objectName = objectName;
        
        this.topleft = topleft;
        this.flip = flip;
        this.rotate = rotate;
        this.scale = scale;

        //get name
        int index = path.lastIndexOf("\\");
        if(index < 0)
            index = path.lastIndexOf("/");
        
        this.name = path.substring(index + 1, path.length());
        //System.out.println("path:" + this.path);
        //System.out.println("name:" + this.name);
    }

    public void set(BufferedImage image, String path, String desc, String objectName, Point topleft, int flip, int rotate, float scale)
    {
        this.image = image;
        this.path = path;
        this.desc = desc;
        this.objectName = objectName;
        
        this.topleft = topleft;
        this.flip = flip;
        this.rotate = rotate;
        this.scale = scale;

        //get name
        int index = path.lastIndexOf("\\");
        if(index < 0)
            index = path.lastIndexOf("/");
        
        this.name = path.substring(index + 1, path.length());        
    }
    
    public static MapItem copy(MapItem original)
    {
        if(original != null)
            return new MapItem(original.image , original.path , original.desc, original.objectName, original.topleft,
                    original.flip, original.rotate, original.scale);
        else
            return null;
    }
}